
#include "member_BTerrain.h"
#include "member_BShape.h"
#include "member_BScene.h"

using namespace BWE;

member_BTerrain::member_BTerrain(BTerrain* terrain)
{
	boss = terrain;
	align = Align_LeftBottom;
	width = 1;
	height = 1;
	skirtHeight = 0;
	elevation.setColumn(3);
	elevation.setRow(3);
	dirtyArea = true;
	dirtyZone = true;
	dirtySkirt = true;
	dirtyTree = true;
	debugIndex = -1;
}
member_BTerrain::~member_BTerrain()
{

}

bool member_BTerrain::rebuild()
{
	const BVector& min = localSpace.min();
	int count = elevation.size();
	if (count == 0)
	{
		boss->clear();
		return true;
	}

	if (!vertices)
	{
		vertices = new BVectorArray();
		normals = new BVectorArray();
		coords = new BCoordArray();
	}

	BReal* zs = elevation.data();
	int column = elevation.column();
	int row = elevation.row();

	if (vertices->size() != count)
	{
		dirtyArea = false;
		vertices->resize(count);
		normals->resize(count);
		coords->resize(count);

		BVector normal(0, 0, 1);
		BReal xunit = width / (column - 1);
		BReal yunit = height / (row - 1);
		float tdx = 1.0f / (column - 1);
		float tdy = 1.0f / (row - 1);
		BReal w = xunit * (column - 1);
		for (int r = 0; r < row; r++)
		{
			int head = r * column;
			BReal y = yunit * r;
			float ty = 1.0f - tdy * r;
			for (int c = 0; c < column; c++)
			{
				int index = head + c;
				BReal x = xunit * c;
				BReal z = zs[index];
				(*vertices)[index] = min + BVector(x, y, z);
				(*normals)[index] = normal;
				(*coords)[index].set(tdx * c, ty);
			}
		}
	}

	int widthSegment = column - 1;
	int heightSegment = row - 1;
	int indexSize = widthSegment * heightSegment * 4;
	if (indices.empty() || indices->size() != indexSize)
	{
		indices = new BIntArray(indexSize);
	}
	for (int r = 0; r < heightSegment; r++)
	{
		int head = r * (widthSegment + 1);
		int pos = r * widthSegment * 4;
		for (int c = 0; c < widthSegment; c++)
		{
			int i0 = head + c;
			int i1 = i0 + 1;
			int i2 = i1 + (widthSegment + 1);
			int i3 = i0 + (widthSegment + 1);
			(*indices)[pos + c * 4] = i0;
			(*indices)[pos + c * 4 + 1] = i1;
			(*indices)[pos + c * 4 + 2] = i2;
			(*indices)[pos + c * 4 + 3] = i3;
		}
	}

	if (dirtyArea)
	{
		BReal xunit = width / (column - 1);
		BReal yunit = height / (row - 1);
		for (int r = 0; r < row; r++)
		{
			int head = r * column;
			BReal y = yunit * r;
			for (int c = 0; c < column; c++)
			{
				int index = head + c;
				BReal x = xunit * c;
				(*vertices)[index].x() = x + min.x();
				(*vertices)[index].y() = y + min.y();
			}
		}
		boss->smooth();
		dirtyArea = false;
	}

	return true;
}

void member_BTerrain::freshQuadTree()
{
	BSpace treeSpace = quadTree->space();
	if (quadTree->empty() || !treeSpace.contain(localSpace))
	{
		quadTree->reset(localSpace);
		for (int i = 0; i < indices->size(); i += 4)
		{
			int ia = indices->at(i);
			int ib = indices->at(i + 1);
			int ic = indices->at(i + 2);
			int id = indices->at(i + 3);
			const BVector& va = vertices->at(ia);
			const BVector& vb = vertices->at(ib);
			const BVector& vc = vertices->at(ic);
			const BVector& vd = vertices->at(id);
			BSpace space(va);
			space.expand(vb);
			space.expand(vc);
			space.expand(vd);

			QuadTreeItem item = quadTree->create(space);
			item->append(ia);
			item->append(ib);
			item->append(ic);
			item->append(id);
		}
	}
}

void member_BTerrain::buildTree(int startIndex, int endIndex)
{
	Node& node = treeNodes[lastIndex];
	int numIndices = endIndex - startIndex;
	if (numIndices == 1)
	{
		node = leafNodes[startIndex];
		lastIndex++;
		return;
	}

	BVector means;
	for (int i = startIndex; i < endIndex; i++)
	{
		means += leafNodes[i].center;
	}
	means /= numIndices;

	BVector variance;
	for (int i = startIndex; i < endIndex; i++)
	{
		BVector diff2 = leafNodes[i].center - means;
		variance += diff2 * diff2;
	}
	variance /= numIndices - 1;

	int splitIndex = startIndex;
	{
		int axis = MaxAxis(variance);
		BReal splitValue = means[axis];
		for (int i = startIndex; i < endIndex; i++)
		{
			const BVector& center = leafNodes[i].center;
			if (center[axis] > splitValue)
			{
				leafNodes.swap(i, splitIndex);
				splitIndex++;
			}
		}
		int numBalanced = numIndices / 3;
		if (splitIndex <= (startIndex + numBalanced) || splitIndex >= (endIndex - 1 - numBalanced))
		{
			splitIndex = startIndex + (numIndices >> 1);
		}
	}

	node.space.min() = localSpace.max();
	node.space.max() = localSpace.min();
	for (int i = startIndex; i < endIndex; i++)
	{
		const BSpace& space = leafNodes[i].space;
		node.space.expand(space);
	}

	int curIndex = lastIndex++;
	buildTree(startIndex, splitIndex);
	buildTree(splitIndex, endIndex);
	node.index = curIndex - lastIndex;
}
void member_BTerrain::overlapping(ITriangleCallback* callback, const BSpace& space)
{
	if (dirtyTree)
	{
		for (int i = 0; i < indices->size(); i += 4)
		{
			int ia = indices->at(i + 0);
			int ib = indices->at(i + 1);
			int ic = indices->at(i + 2);
			int id = indices->at(i + 3);
			const BVector& va = vertices->at(ia);
			const BVector& vb = vertices->at(ib);
			const BVector& vc = vertices->at(ic);
			const BVector& vd = vertices->at(id);
			Node& node = leafNodes.append();
			node.space.reset(va);
			node.space.expand(vb);
			node.space.expand(vc);
			node.space.expand(vd);
			node.center = node.space.center();
			node.index = i;
		}
		if (leafNodes.size())
		{
			lastIndex = 0;
			treeNodes.resize(2 * leafNodes.size());
			buildTree(0, leafNodes.size());
		}
		leafNodes.clear();
		dirtyTree = false;
	}
	int index = 0;
	while (index < lastIndex)
	{
		const Node& node = treeNodes[index];
		bool overlapped = space.intersect(node.space);
		if (overlapped && node.index >= 0)
		{
			processNode(callback, node.index);
		}
		if (overlapped || node.index >= 0)
		{
			index++;
		}
		else
		{
			index -= node.index;
		}
	}
}
void member_BTerrain::overlapping(ITriangleCallback* callback, const BLine& line)
{
	int index = 0;
	while (index < lastIndex)
	{
		const Node& node = treeNodes[index];
		bool overlapped = line.intersect(node.space);
		if (overlapped && node.index >= 0)
		{
			processNode(callback, node.index);
		}
		if (overlapped || node.index >= 0)
		{
			index++;
		}
		else
		{
			index -= node.index;
		}
	}
}
void member_BTerrain::processNode(ITriangleCallback* callback, int index)
{
	const BVectorArray* vertices = boss->vertices();
	int ia = indices->at(index + 0);
	int ib = indices->at(index + 1);
	int ic = indices->at(index + 2);
	int id = indices->at(index + 3);
	BTriangle triangle;
	triangle[0] = vertices->at(ia);
	triangle[1] = vertices->at(ib);
	triangle[2] = vertices->at(ic);
	callback->process(triangle, 0);
	triangle[0] = vertices->at(ia);
	triangle[1] = vertices->at(ic);
	triangle[2] = vertices->at(id);
	callback->process(triangle, 0);
}
